#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/unistd.h>
#include <linux/ioport.h>

#include <asm/system.h>
#include <asm/uaccess.h>	// copy_from_user
#include <asm/io.h>		// inb, outb

# define	PORTADR	(0x3F8)
# define	DATA	(PORTADR + 0x1)
# define	STATUS	(PORTADR + 0x2)
# define	CONTROL	(PORTADR + 0x3)

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Quentin");
MODULE_DESCRIPTION("SERIALPORT");

int				major;
char*				name = "mydev";
char*				namee = "mydev\0";
dev_t*				devt;
struct class*			classdev;
struct cdev*			scdev;

# define			namelen strlen(namee)

int	modk_open(struct inode *inode, struct file *filp)
{
  printk("modk_open() called.\n");
  return 0;
}

ssize_t	modk_read(struct file *filp, char __user *buf, size_t count
		  , loff_t *f_pos)
{
  /*
  int	len = (count > namelen ? namelen : count);

  strncpy(buf, namee, len);
  printk("modk_read() called : %d\n", count);
  */
  static int	reading = 1;
  char*		str;
  int	i = 0;

  if ((str = kmalloc(namelen, GFP_KERNEL)) == NULL)
    return 0;
  if (reading)
    {
      for (; i < count && i < namelen; ++i)
	{
	  outb(namee[i], 0x3f8);
	  buf[i] = namee[i];
	  str[i] = namee[i];
	}
      printk("modk_write() : %s.\n", str);
      reading = 0;
    }
  else
    {
      reading = 1;
      return 0;
    }
  return i;
}

int	modk_release(struct inode *inode, struct file *filp)
{
  printk("modk_release() called.\n");
  return 0;
}

ssize_t	modk_write(struct file *filp, const char __user *buf
		   , size_t count, loff_t *f_pos)
{
  char*	str = kmalloc(count + 1, GFP_KERNEL);	// device structure
  int	i = 0;

  if (str == NULL)
    return 0;
  for (; i < count; ++i)
      str[i] = inb(0x3f8);
  str[i] = '\0';
  printk("modk_write() : %s.\n", str);
  return count;
}


struct file_operations		fsops =
  {
    .owner = THIS_MODULE,
    .read = modk_read,
    .write = modk_write,
    .open = modk_open,
    .release = modk_release,
  };

int				init_module()
{
  /*
  devt = MKDEV();
  mknod(name, 0755, devt);
  major = register_chrdev(0, name, &fsops);*/
  devt = kmalloc(sizeof(dev_t), GFP_KERNEL);	// device structure
  if (alloc_chrdev_region(devt, 0, 1, name) == 0)// ask for 1 new device
    return -1;
  classdev = class_create(THIS_MODULE, name);	// class structure for module

  if ((scdev = cdev_alloc()) != NULL)		// cdev structure
    {
      scdev->ops = &fsops;
      scdev->owner = THIS_MODULE;
      cdev_init(scdev, &fsops);
      if (0 == cdev_add(scdev, *devt, 1))	// add a char device
	{
	  device_create(classdev, NULL, *devt, NULL, name);	// create device
	  //	  ioperm(0x3f8, 1, 1);					// allow access to serial port
	  printk("Loaded module modk\n");
	}
    }
  return 0;
}

void            cleanup_module()
{
  printk("Unloading module modk");
  if (scdev)
    {
      device_destroy(classdev, *devt);
      cdev_del(scdev);
    }
  class_destroy(classdev);
  unregister_chrdev_region(*devt, 1);
}
